/***********************************************************************************************************************
*                                                                                                                      *
* SPLASH build system v0.1                                                                                             *
*                                                                                                                      *
* Copyright (c) 2013 Andrew D. Zonenberg                                                                               *
* All rights reserved.                                                                                                 *
*                                                                                                                      *
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the     *
* following conditions are met:                                                                                        *
*                                                                                                                      *
*    * Redistributions of source code must retain the above copyright notice, this list of conditions, and the         *
*      following disclaimer.                                                                                           *
*                                                                                                                      *
*    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the       *
*      following disclaimer in the documentation and/or other materials provided with the distribution.                *
*                                                                                                                      *
*    * Neither the name of the author nor the names of any contributors may be used to endorse or promote products     *
*      derived from this software without specific prior written permission.                                           *
*                                                                                                                      *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   *
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL *
* THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES        *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR       *
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE       *
* POSSIBILITY OF SUCH DAMAGE.                                                                                          *
*                                                                                                                      *
***********************************************************************************************************************/

#include "splashcore.h"

using namespace std;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// NocgenQuadtreeNode

NocgenQuadtreeNode::NocgenQuadtreeNode(string name)
	: m_name(name)
	, m_baseaddr(0)
	, m_subnetsize(1)
	, m_rpc(false)
	, m_dma(false)
{
	for(int i=0; i<4; i++)
		m_children[i] = NULL;
}

NocgenQuadtreeNode::~NocgenQuadtreeNode()
{
	//Delete routers, but leave nodes managed separately
	for(int i=0; i<4; i++)
	{
		if(dynamic_cast<NocgenRouter*>(m_children[i]) != NULL)
			delete m_children[i];
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// NocgenRouter

NocgenRouter::NocgenRouter(uint16_t baseaddr, uint16_t subnetsize)
{
	m_baseaddr = baseaddr;
	m_subnetsize = subnetsize;
	
	unsigned int prefix_len = 16;
	unsigned int ntmp = subnetsize;
	while(ntmp > 1)
	{
		prefix_len --;
		ntmp /= 2;
	}
	
	//printf("creating router at subnet %04x prefix %d\n", baseaddr, prefix_len);
	
	char tmp[128];
	snprintf(tmp, sizeof(tmp), "router_%04x_%d", baseaddr, prefix_len);
	m_name = tmp;
}

NocgenRouter::~NocgenRouter()
{
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Construction / destruction

Nocgenv2GeneratedSourceNode::Nocgenv2GeneratedSourceNode(
	BuildGraph* graph, CodeGenScriptNode* src, CodeGenScriptNode* bsp, string fname)
	: VerilogGeneratedSourceNode(graph, fname)
	, m_srcfile(src)
	, m_bspfile(bsp)
	, m_rootRouter(NULL)
{
	m_dependencies.push_back(src);
	
	//Zeroize nameserver key by default
	memset(m_namesrvrKey, 0, sizeof(m_namesrvrKey));
	
	//Add the name server and system info nodes
	m_namesrvr = new NocgenNode("namesrvr", "NOCNameServer", true, false);
	m_nodes.push_back(m_namesrvr);
	m_sysinfo = new NocgenNode("sysinfo", "NOCSysinfo", true, false);
	m_nodes.push_back(m_sysinfo);
	
	//By default, nameserver does not allow writes
	m_namesrvr->m_params["READ_ONLY"] = "1'b1";
	
	//Parse the BSP
	vector<string> bsplines;
	ReadLines(bsp->GetOutputPath(), bsplines);
	ParseBSP(bsp->GetOutputPath(), bsplines);
		
	//Parse the script
	vector<string> scriptlines;
	ReadLines(src->GetOutputPath(), scriptlines);
	ParseScript(src->GetOutputPath(), scriptlines);
	
	//Generate the full code
	GenerateNetworkTopology();
	GenerateCode();

	//Write the HDL file
	string ofname = GetBinaryPath();
	MakeDirectoryRecursive(GetDirOfFile(ofname), 755);
	DebugPrintfHeading(3, "Generating top-level HDL file %s\n", m_fname.c_str());
	FILE* fp = fopen(ofname.c_str(), "w");
	if(!fp)
		FatalError("Failed to create nocgen script %s\n", ofname.c_str());
	fputs(m_code.c_str(), fp);
	fclose(fp);
	
	//Create the constraints
	ofname = GetConstraintsPath();
	MakeDirectoryRecursive(GetDirOfFile(ofname), 755);
	fp = fopen(ofname.c_str(), "w");
	if(!fp)
		FatalError("Failed to create nocgen constraints %s\n", ofname.c_str());
	fputs(m_constraints.c_str(), fp);
	fclose(fp);
	
	//Write the Graphviz file
	ofname = GetDotPath();
	MakeDirectoryRecursive(GetDirOfFile(ofname), 755);
	fp = fopen(ofname.c_str(), "w");
	if(!fp)
		FatalError("Failed to create nocgen constraints %s\n", ofname.c_str());
	fputs(m_dotcode.c_str(), fp);
	fclose(fp);
	
	//Loop over our nodes and search their parameters
	for(auto node : m_nodes)
	{
		//Skip dependencies of auto-generated nodes
		if(node == m_sysinfo)
			continue;
		if(node == m_namesrvr)
			continue;
		
		for(auto it : node->m_params)
		{
			string param = it.second;
			
			//See if it's a filename in our output directory
			string outdir = m_graph->GetOutDir();
			if(param.find(outdir) == string::npos)
				continue;
			if(param.find("nocgen/") != string::npos)
				continue;
				
			//It's a file we generated (possibly automatically)
			//Remove the path prefix and quotes
			param = str_replace("\"", "",	param);
			param = str_replace(outdir, "",	param);
			
			//Look up the architecture and file
			char arch[512] = "";
			char fname[512] = "";
			if(2 != sscanf(param.c_str(), "/%511[^/]/%511s", arch, fname))
				continue;

			//Look up the target
			string sfname = fname;
			sfname = str_replace(".hex", "-hex", sfname);
			if(!graph->IsTargetForArch(sfname, arch))
				continue;
			m_dependencies.push_back(graph->GetTargetForArch(sfname, arch));
		}
	}	
}

Nocgenv2GeneratedSourceNode::~Nocgenv2GeneratedSourceNode()
{
	//Get rid of routers
	if(m_rootRouter != NULL)
	{
		delete m_rootRouter;
		m_rootRouter = NULL;
	}
	
	//Get rid of PLLs
	for(auto x : m_plls)
		delete x;
	m_plls.clear();
	
	//Get rid of nodes
	for(auto x : m_nodes)
		delete x;
	m_nodes.clear();
}

void Nocgenv2GeneratedSourceNode::ReadLines(string fname, vector<string>& lines)
{
	char line[1024];
	FILE* fp = fopen(fname.c_str(), "r");
	if(!fp)
		FatalError("Could not open nocgen file %s\n", fname.c_str());
	while(NULL != fgets(line, sizeof(line), fp))
	{
		//Remove C++ comments and leading whitespace
		char* pline = line;
		char* pcom = strstr(pline, "//");
		if(pcom != NULL)
			*pcom = '\0';
		while(isspace(*pline))
			pline ++;
			
		//Remove trailing whitespace
		for(int x=strlen(pline)-1; x >= 0; x--)
		{
			if(isspace(pline[x]))
				pline[x] = '\0';
			else
				break;
		}
			
		//Skip blank lines
		if(strlen(pline) == 0)
			continue;
		
		lines.push_back(pline);
	}
	fclose(fp);
}

void Nocgenv2GeneratedSourceNode::ParseBSP(string fname, const vector<string>& bsplines)
{
	char tmp[128] = "";
	char tmp2[128] = "";
	char tmp3[128] = "";
	char tmp4[128] = "";
	char dir[128] = "";
	for(auto line : bsplines)
	{
		//Look up the opcode
		sscanf(line.c_str(), "%127s", tmp);
		string sopcode(tmp);
		
		//Parse it
		if(sopcode == "target")
		{
			//TODO: read scan position
			if(1 == sscanf(line.c_str(), "target %127s", tmp))
				m_device = tmp;
			else
				FatalError("Bad \"target\" line \"%s\" in bsp file \"%s\"\n", line.c_str(), fname.c_str());
		}
		else if(sopcode == "clock")
		{
			if(4 != sscanf(line.c_str(), "clock %127s input %127s std %127s freq %127s", tmp, tmp2, tmp3, tmp4))
				FatalError("Bad \"clock\" line \"%s\" in bsp file \"%s\"", line.c_str(), fname.c_str());
			
			//Parse out the frequency
			int ifreq;
			char fmult;
			if(2 != sscanf(tmp4, "%d%c", &ifreq, &fmult))
				FatalError("Bad clock frequency \"%s\" in bsp file \"%s\"\n", tmp4, fname.c_str());
			if(fmult == 'M')
				ifreq *= 1000000;
			else
				FatalError("Bad clock frequency \"%s\" in bsp file \"%s\"\n", tmp4, fname.c_str());
				
			//Verify it doesnt already exist
			if(m_bsppins.find(tmp) != m_bsppins.end())
				FatalError("Attempted redeclaration of signal \"%s\" in bsp file \"%s\"\n", tmp, fname.c_str());
				
			//Create the pin
			m_bsppins[tmp] = NocgenBSPPin(tmp, tmp2, tmp3, NocgenBSPPin::DIR_IN);
				
			//Save the frequency
			m_bspclocks[tmp] = NocgenClockNet(tmp, NocgenClockNet::CLOCK_GLOBAL, ifreq);
		}
		else if(sopcode == "signal")
		{
			//Parse the fixed fields
			int nfield = sscanf(line.c_str(),
				"signal %127s %127s %127s std %127s %127[^\n]", tmp, dir, tmp2, tmp3, tmp4);
				
			//Complain if missing fields
			if( (nfield != 4) && (nfield != 5) )
				FatalError("Bad \"signal\" line \"%s\" in bsp file \"%s\"\n", line.c_str(), fname.c_str());
			
			//Verify it doesnt already exist
			if(m_bsppins.find(tmp) != m_bsppins.end())
				FatalError("Attempted redeclaration of signal \"%s\" in bsp file \"%s\"\n", tmp, fname.c_str());
			
			//Set up the direction
			NocgenBSPPin::pin_direction ndir = NocgenBSPPin::DIR_IN;
			string sdir(dir);
			if(sdir == "output")
				ndir = NocgenBSPPin::DIR_OUT;
			else if(sdir == "input")
				ndir = NocgenBSPPin::DIR_IN;
			else if(sdir == "inout")
				ndir = NocgenBSPPin::DIR_INOUT;
			else
				FatalError("bad pin direction %s\n", dir);
			
			//Tokenize additional flags
			string stmp;
			vector<string> flags;
			if(nfield == 5)
			{
				for(size_t i=0; i<sizeof(tmp4) && tmp4[i]; i++)
				{
					if(isspace(tmp4[i]))
					{
						if(stmp != "")
							flags.push_back(stmp);
						stmp = "";
					}
					else
						stmp += tmp4[i];
				}
				if(stmp != "")
					flags.push_back(stmp);
			}
			
			//Parse additional flags
			unsigned int speed = NocgenBSPPin::DRIVE_DEFAULT;
			unsigned int term = NocgenBSPPin::TERM_NONE;
			unsigned int pull = NocgenBSPPin::PULL_NONE;
			for(size_t i=0; i<flags.size(); i++)
			{
				const string& flag = flags[i];
				if(flag == "pullup")
					pull = NocgenBSPPin::PULL_UP;
				else if(flag == "pulldown")
					pull = NocgenBSPPin::PULL_DOWN;
				else if(flag == "fast")
					speed = NocgenBSPPin::DRIVE_FAST;
				else if(flag == "term" && (i+1) < flags.size())
				{
					int n = atoi(flags[++i].c_str());
					switch(n)
					{
					case 25:
						term = NocgenBSPPin::TERM_APPROX_25;
						break;
					case 50:
						term = NocgenBSPPin::TERM_APPROX_50;
						break;
					case 75:
						term = NocgenBSPPin::TERM_APPROX_75;
						break;
					default:
						FatalError("Invalid termination impedance %d\n", n);
					}
				}
				else
					FatalError("Unknown BSP flag %s (line = %s, file = %s)\n",
						flag.c_str(), line.c_str(), fname.c_str());
			}
			
			//Create the pin
			m_bsppins[tmp] = NocgenBSPPin(tmp, tmp2, tmp3, ndir, pull, speed, term);
		}
		else
			FatalError("Unrecognized opcode \"%s\" in bsp file \"%s\"\n", sopcode.c_str(), fname.c_str());
	}
	
	//Add a clock for TCK
	string tck = "debug_controller/jtag_tck";
	switch(GetDeviceFamily())
	{
		case FAMILY_SPARTAN6:
			m_bspclocks[tck] = NocgenClockNet(tck, NocgenClockNet::CLOCK_GLOBAL, 33 * 1000 * 1000);
			break;
			
		case FAMILY_ARTIX7:
		case FAMILY_KINTEX7:
			m_bspclocks[tck] = NocgenClockNet(tck, NocgenClockNet::CLOCK_GLOBAL, 66 * 1000 * 1000);
			break;
			
		default:
			FatalError("this device family is not supported (TCK)");
			break;
	}
	m_clocknets[tck] = m_bspclocks[tck];
		
		
	//Add a cross-clock constraint between TCK and the NoC clock domain
	m_crossclocks.push_back(pair<string, string>(tck, "clk_noc"));
}

void Nocgenv2GeneratedSourceNode::ParseScript(string fname, const vector<string>& scriptlines)
{
	char tmp[128] = "";
	char tmp2[128] = "";
	int tmp3;
	for(unsigned int i=0; i<scriptlines.size(); i++)
	{
		string line = scriptlines[i];
		
		//Look up the opcode
		sscanf(line.c_str(), "%127s", tmp);
		string sopcode(tmp);
		
		//Top-level module info
		if(sopcode == "top")
		{
			if(1 == sscanf(line.c_str(), "top %127s", tmp))
				m_toplevel = tmp;
			else
				FatalError("Bad \"top\" line \"%s\" in nocgen file \"%s\"\n", line.c_str(), fname.c_str());
		}
		
		//A PLL for clock synthesis
		else if(sopcode == "pll")
			m_plls.push_back(new NocgenPLL(scriptlines, i, this, fname));
			
		//A NoC node
		else if(sopcode == "node")
		{
			NocgenNode* node = new NocgenNode(scriptlines, i, this, fname);
			m_nodes.push_back(node);
			
			//Add bus widths for each signal for the LA's benefit
			snprintf(tmp, sizeof(tmp), "%s_rpc_tx_en", node->m_name.c_str());
			m_buswidths[tmp] = 1;
			snprintf(tmp, sizeof(tmp), "%s_rpc_rx_en", node->m_name.c_str());
			m_buswidths[tmp] = 1;
			snprintf(tmp, sizeof(tmp), "%s_rpc_tx_ack", node->m_name.c_str());
			m_buswidths[tmp] = 2;
			snprintf(tmp, sizeof(tmp), "%s_rpc_rx_ack", node->m_name.c_str());
			m_buswidths[tmp] = 2;
			snprintf(tmp, sizeof(tmp), "%s_rpc_tx_data", node->m_name.c_str());
			m_buswidths[tmp] = 32;
			snprintf(tmp, sizeof(tmp), "%s_rpc_rx_data", node->m_name.c_str());
			m_buswidths[tmp] = 32;
			snprintf(tmp, sizeof(tmp), "%s_dma_tx_en", node->m_name.c_str());
			m_buswidths[tmp] = 1;
			snprintf(tmp, sizeof(tmp), "%s_dma_rx_en", node->m_name.c_str());
			m_buswidths[tmp] = 1;
			snprintf(tmp, sizeof(tmp), "%s_dma_tx_ack", node->m_name.c_str());
			m_buswidths[tmp] = 1;
			snprintf(tmp, sizeof(tmp), "%s_dma_rx_ack", node->m_name.c_str());
			m_buswidths[tmp] = 1;
			snprintf(tmp, sizeof(tmp), "%s_dma_tx_data", node->m_name.c_str());
			m_buswidths[tmp] = 32;
			snprintf(tmp, sizeof(tmp), "%s_dma_rx_data", node->m_name.c_str());
			m_buswidths[tmp] = 32;
		}
			
		//A logic analyzer
		else if(sopcode == "la")
			m_nodes.push_back(new NocgenLA(scriptlines, i, this, fname));
			
		//A packet sniffer
		else if(sopcode == "sniffer")
			m_nodes.push_back(new NocgenSniffer(scriptlines, i, this, fname));
		
		//The nameserver key
		else if(sopcode == "namesrvr_key")
		{
			if(1 != sscanf(line.c_str(), "namesrvr_key \"%127[^\"]\"", tmp))
				FatalError("Bad \"namesrvr_key\" line \"%s\" in nocgen file \"%s\"\n", line.c_str(), fname.c_str());
				
			//Hash the string to generate a 512-bit key
			CryptoPP::SHA512().CalculateDigest(m_namesrvrKey, (unsigned char*)tmp, strlen(tmp));
			
			//Not read only, update the HMAC key
			m_namesrvr->m_params["READ_ONLY"] = "1'b0";
			for(int j=0; j<16; j++)
			{
				snprintf(tmp, sizeof(tmp), "HMAC_KEY_%X", j);
				snprintf(tmp2, sizeof(tmp2), "32'h%02x%02x%02x%02x",
					m_namesrvrKey[j*4] & 0xff,
					m_namesrvrKey[j*4 + 1] & 0xff,
					m_namesrvrKey[j*4 + 2] & 0xff,
					m_namesrvrKey[j*4 + 3] & 0xff);
				m_namesrvr->m_params[tmp] = tmp2;
			}
		}
			
		//Constant drivers
		else if(sopcode == "constant")
		{
			if(2 != sscanf(line.c_str(), "constant %d %127s", &tmp3, tmp))
				FatalError("Bad \"constant\" line \"%s\" in nocgen file \"%s\"\n", line.c_str(), fname.c_str());
				
			//Add to list
			m_constants[tmp] = tmp3;
			
			//Add to list of pins
			AddPinFromBSP(tmp, NocgenBSPPin::DIR_OUT);
		}
		
		//Reference clock for the I/O delay lines
		else if(sopcode == "iodelay_refclk")
		{
			//TODO: Support more than one, save frequency
			if(1 == sscanf(line.c_str(), "iodelay_refclk %127s", tmp))
				m_iodelayRefclk = tmp;
			else
				FatalError("Bad \"iodelay_refclk\" line \"%s\" in bsp file \"%s\"\n", line.c_str(), fname.c_str());
		}
			
		//Cross-clock constraints
		else if(sopcode == "crossclock")
		{
			if(2 != sscanf(line.c_str(), "crossclock %127s %127s", tmp, tmp2))
				FatalError("Bad \"crossclock\" line \"%s\" in nocgen file \"%s\"\n", line.c_str(), fname.c_str());
			m_crossclocks.push_back( pair<string, string>(tmp, tmp2) );
		}
		
		else if(sopcode == "floorplan")
		{
			if(2 != sscanf(line.c_str(), "floorplan %127s %127s", tmp, tmp2))
				FatalError("Bad floorplan line in nocgen file \"%s\"\n", line.c_str(), fname.c_str());
			m_latefloorplans[tmp].push_back(tmp2);
		}
		
		//GTP clock inputs
		else if(sopcode == "gtpclk")
		{
			if(1 != sscanf(line.c_str(), "gtpclk %127s", tmp))
				FatalError("Bad \"gtpclk\" line \"%s\" in nocgen file \"%s\"\n", line.c_str(), fname.c_str());
			m_gtpclocks.push_back(tmp);
			
			//Format names for the clock nets
			string diffin_p = string(tmp) + "_p";
			string diffin_n = string(tmp) + "_n";
			
			//Verify it exists, and is a valid clock net
			if(!IsBSPClockNet(diffin_p))
			{
				FatalError("Differential GTP input \"%s\" is not a BSP clock net (in gtpclk block of nocgen file \"%s\")\n",
					diffin_p.c_str(), tmp, fname.c_str());
			}
			if(!IsBSPClockNet(diffin_n))
			{
				FatalError("Differential GTP input \"%s\" is not a BSP clock net (in gtpclk block of nocgen file \"%s\")\n",
					diffin_n.c_str(), tmp, fname.c_str());
			}
			
			//Add the P/N clocks as input pins
			AddPinFromBSP(diffin_p, NocgenBSPPin::DIR_IN);
			AddPinFromBSP(diffin_n, NocgenBSPPin::DIR_IN);
			
			//Create a new clock based on the two inputs
			NocgenClockNet input_p = GetBSPClockNet(diffin_p);
			AddClockNet(tmp, NocgenClockNet(
				tmp,
				NocgenClockNet::CLOCK_GLOBAL,
				input_p.m_freq,
				input_p.m_duty,
				input_p.m_phase,
				""));
			
			//Set 1ps jitter for GTP reference clock
			m_clocknets[tmp].m_jitter = 1;
		}
		
		//Additional dependencies
		else if(sopcode == "dependency")
		{
			if(1 != sscanf(line.c_str(), "dependency %127[^\n]", tmp))
				FatalError("Bad \"dependency\" line \"%s\" in nocgen file \"%s\"\n", line.c_str(), fname.c_str());
			m_dtargets.push_back(tmp);
		}
		
		//Timing-ignore constraints
		else if(sopcode == "tig")
		{
			if(1 != sscanf(line.c_str(), "tig %127[^\n]", tmp))
				FatalError("Bad \"tig\" line \"%s\" in nocgen file \"%s\"\n", line.c_str(), fname.c_str());
			m_tigs.push_back(tmp);
		}
		
		//Internally generated clock
		else if(sopcode == "selfclock")
		{
			if(1 != sscanf(line.c_str(), "selfclock %127s", tmp))
				FatalError("Bad \"selfclock\" line \"%s\" in nocgen file \"%s\"\n", line.c_str(), fname.c_str());
			
			//Verify it's not already used
			if(m_selfclock != "")
				FatalError("Internal clock is already being used");
				
			//Generate the clock signal
			//Use worst-case CCLK frequency for static timin
			switch(GetDeviceFamily())
			{
				case FAMILY_SPARTAN6:
					AddClockNet(tmp,
						NocgenClockNet(tmp, NocgenClockNet::CLOCK_GLOBAL, 60 * 1000 * 1000));	//40 MHz +/- 50%
					break;
				
				case FAMILY_ARTIX7:
				case FAMILY_KINTEX7:
					AddClockNet(tmp,
						NocgenClockNet(tmp, NocgenClockNet::CLOCK_GLOBAL, 98 * 1000 * 1000));	//65 MHz +/- 50%
					break;
					
				default:
					FatalError("Internal clock support not implemented for this device family\n");
			}
			
			m_selfclock = tmp;
		}
			
		else
			FatalError("Unrecognized opcode \"%s\" in nocgen file \"%s\"\n", sopcode.c_str(), fname.c_str());
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Code generation etc

void Nocgenv2GeneratedSourceNode::GenerateNetworkTopology()
{
	//The working set of nodes at the CURRENT tree level
	vector<NocgenQuadtreeNode*> current_level_nodes;
	
	//The working set of nodes at the NEXT tree level
	vector<NocgenQuadtreeNode*> next_level_nodes;

	//Loop over tree levels until we hit the root
	unsigned int nodes_allocated = 0;
	unsigned int subnetsize = 4;		//number of addresses in one subnet
	unsigned int nodesize = 1;			//number of addresses in one node (always 1/4 of subnetsize)
	uint16_t node_addr = 0x8000;
	uint16_t router_addr = 0x8000;
	//printf("nocgen: Generating %s\n", m_fname.c_str());
	while( (nodes_allocated < m_nodes.size()) || current_level_nodes.size() > 1 )
	{	
		//Reset router subnet
		router_addr = 0x8000;
		
		//printf("    nocgen: Allocating addresses to nodes at subnet size %u starting at 0x%04x\n", subnetsize, node_addr);
		
		//Pack in all nodes at the current tree level after the child routers from the previous level
		for(unsigned int i=0; i<m_nodes.size(); i++)
		{
			if(m_nodes[i]->m_subnetsize == nodesize)
				current_level_nodes.push_back(m_nodes[i]);
		}
		
		//Allocate addresses for each node and assign them to routers
		NocgenRouter* router = new NocgenRouter(router_addr, subnetsize);
		for(unsigned int i=0; i<current_level_nodes.size(); i++)
		{
			//Pack in the node on one port
			NocgenQuadtreeNode* node = current_level_nodes[i];
			router->m_children[i % 4] = node;
			
			//If the child has RPC or DMA, the parent does too
			if(node->m_rpc)
				router->m_rpc = true;
			if(node->m_dma)
				router->m_dma = true;
			
			//Allocate an address iff it isn't a router
			if(dynamic_cast<NocgenNode*>(node) != NULL)
			{
				//if(subnetsize != 1)
				//	printf("         nocgen: Assigning address %04x to node %s\n", node_addr, node->m_name.c_str());
				node->m_baseaddr = node_addr;
				node_addr += nodesize;
				
				nodes_allocated ++;
			}
			
			//If we've filled the router, and there's more nodes left, pack it and continue
			if( ((i % 4) == 3) && (i+1 < current_level_nodes.size()) )
			{
				next_level_nodes.push_back(router);
				router_addr += subnetsize;
				router = new NocgenRouter(router_addr, subnetsize);
			}
		}
		next_level_nodes.push_back(router);
		
		//Get ready to go on to the next level
		subnetsize *= 4;
		nodesize *= 4;
		if(node_addr % nodesize)
			node_addr = node_addr - (node_addr % nodesize) + nodesize;
		current_level_nodes = next_level_nodes;
		next_level_nodes.clear();
		
		//printf("        nodes allocated = %d nodes.size() = %d\n", nodes_allocated, m_nodes.size());
	}
	
	//Done, we have only one node
	m_rootRouter = dynamic_cast<NocgenRouter*>(current_level_nodes[0]);
}

void Nocgenv2GeneratedSourceNode::GenerateCode()
{
	//Add info for sysinfo
	if(m_clocknets.find("clk_noc") == m_clocknets.end())
		FatalError("no clk_noc found");
	NocgenClockNet clk = m_clocknets["clk_noc"];
	float period = 1.0e12f / clk.m_freq;		//in picoseconds
	
	char tmp[512];
	snprintf(tmp, sizeof(tmp), "%.0f", period);
	m_sysinfo->m_params["sysclk_period"] = tmp;
	snprintf(tmp, sizeof(tmp), "%d", clk.m_freq);
	m_sysinfo->m_params["sysclk_hz"] = tmp;
	
	//Generate nameserver table
	GenerateNameserverTable();
	
	//Add info for nameserver
	m_namesrvr->m_params["host_mem_file"] = string("\"") + GetNameserverHostPath() + "\"";
	m_namesrvr->m_params["addr_mem_file"] = string("\"") + GetNameserverAddrPath() + "\"";
	
	//Format ports
	string top_pins = "";
	string port_decls = "";
	for(auto x : m_toppins)
	{
		string name = x.first;
		
		//Check if the pin is part of a bus
		int buswidth = 1;
		if(name.find("[") != string::npos)
		{
			//If this isn't the 0th entry, skip it
			string base = name.substr(0, name.find("["));
			if(name.find("[0]") == string::npos)
				continue;
				
			//Save base name and look up width for later			
			name = base;
			buswidth = m_buswidths[name];
		}
		
		//Add to top level ports iff we're not a debug signal
		if(x.second.m_dir != NocgenBSPPin::DIR_NC)
		{
			if(top_pins != "")
				top_pins += ",\n";
			top_pins += string("    ") + name;
		}
		
		switch(x.second.m_dir)
		{
		case NocgenBSPPin::DIR_IN:
			port_decls += "\tinput wire";
			break;
		case NocgenBSPPin::DIR_OUT:
			port_decls += "\toutput wire";
			break;
		case NocgenBSPPin::DIR_INOUT:
			port_decls += "\tinout wire";
			break;
		case NocgenBSPPin::DIR_NC:
			port_decls += "\twire";
			break;
		default:
			FatalError("bad direction");
		}
		
		//Add bus declaration
		if(buswidth > 1)
		{
			snprintf(tmp, sizeof(tmp), "[%d:0]", buswidth-1);
			port_decls += tmp;
		}
		
		port_decls += string(" ") + name + ";\n";
	}
	
	//Read the top of the file and add ports
	m_code = template_get_contents("file_top.v");
	m_code = str_replace("[[module_name]]", m_toplevel, m_code);
	m_code = str_replace("[[ports]]", top_pins, m_code);
	m_code = str_replace("[[port_decls]]", port_decls, m_code);
	
	//Add constant drivers
	string constant_drivers;
	for(auto x : m_constants)
	{
		snprintf(tmp, sizeof(tmp), "\tassign %s = %d;\n", x.first.c_str(), x.second);
		constant_drivers += tmp;
	}
	m_code = str_replace("[[constant_drivers]]", constant_drivers, m_code);
	
	//If we are using the CCLK pin or the internal oscillator, instantiate the startup block
	string startupblock;
	if( (m_selfclock != "")  || (m_cclknet != "") )
	{
		//Declare the output clock
		if(m_selfclock != "")
		{
			snprintf(tmp, sizeof(tmp), "\twire %s;\n", m_selfclock.c_str());
			startupblock += tmp;
		}
		
		//Declare CCLK pin
		if(m_cclknet != "")
		{
			snprintf(tmp, sizeof(tmp), "\twire %s;\n", m_cclknet.c_str());
			startupblock += tmp;
		}
		
		switch(GetDeviceFamily())
		{
			case FAMILY_SPARTAN6:
				startupblock += template_get_contents("configblock_s6.v");
				break;
			
			case FAMILY_ARTIX7:
			case FAMILY_KINTEX7:
				startupblock += template_get_contents("configblock_7series.v");
				break;
			
			default:
				FatalError("Startup block not implemented for this device family (%d)\n", GetDeviceFamily());
		}
		
		startupblock = str_replace("[[selfclock]]", m_selfclock, startupblock);
		
		if(m_cclknet != "")
		{
			startupblock = str_replace("[[cclk_highz]]", "1'b0", startupblock);
			startupblock = str_replace("[[cclk_netname]]", m_cclknet, startupblock);
		}
		else
		{
			startupblock = str_replace("[[cclk_highz]]", "1'b1", startupblock);
			startupblock = str_replace("[[cclk_netname]]", "", startupblock);
		}
	}
	m_code = str_replace("[[startup]]", startupblock, m_code);
	
	//Add PLLs and GTP input buffers
	string plls;
	for(auto x : m_plls)
		plls += x->GenerateCode(this);
	for(auto x : m_gtpclocks)
	{
		string inbuf = "";
		switch(GetDeviceFamily())
		{
			case FAMILY_ARTIX7:
				inbuf = template_get_contents("gtp_clkbuf_7series.v");
				break;
			
			default:
				FatalError("SERDES clock buffer not implemented for this device family (%d)\n", GetDeviceFamily());
		}
		plls += str_replace("[[name]]", x, inbuf);
	}
	m_code = str_replace("[[plls]]", plls, m_code);
	
	//Add I/O delay calibration logic if necessary
	string delaycal;
	if(m_iodelayRefclk != "")
	{
		//Look up the clock
		if(m_clocknets.find(m_iodelayRefclk) == m_clocknets.end())
			FatalError("I/O delay reference clock \"%s\" does not exist\n", m_iodelayRefclk.c_str());
		if(m_clocknets[m_iodelayRefclk].m_freq != 200000000)
			FatalError("I/O delay reference clock \"%s\" must be 200 MHz\n", m_iodelayRefclk.c_str());
			
		//Create the control block
		switch(GetDeviceFamily())
		{
			case FAMILY_ARTIX7:
			case FAMILY_KINTEX7:
				delaycal = template_get_contents("iodelay_control.v");
				break;
				
			case FAMILY_SPARTAN6:
				//no action needed, there isn't any reference clock for spartan6 uncalibrated delays
				break;
				
			default:
				FatalError("I/O delay calibration not implemented for this device family (%d)\n", GetDeviceFamily());
				break;
		}
		
		delaycal = str_replace("[[refclk]]", m_iodelayRefclk, delaycal);
	}
	m_code = str_replace("[[delaycal]]", delaycal, m_code);
	
	//Add routers and nodes
	GenerateRouters();
	
	//TODO: Decide what kind of constraints to target
	bool target_is_ucf = true;
	
	//Generate the constraint file
	string node_pins;
	string input_clocks;
	string crossclocks;
	string floorplans;
	string tigs;
	if(target_is_ucf)
	{
		m_constraints = template_get_contents("ucf_constraint_skeleton.ucf");
				
		//Append constraints for our individual pins
		for(auto it : m_toppins)
		{
			//Pin isn't bonded out (internal debug net), ignore it
			if(it.second.m_dir == NocgenBSPPin::DIR_NC)
				continue;
			
			string smod = "";
			
			//Pullup/down
			switch(it.second.m_pull)
			{
			case NocgenBSPPin::PULL_NONE:
				break;
			case NocgenBSPPin::PULL_DOWN:
				smod += " | PULLDOWN";
				break;
			case NocgenBSPPin::PULL_UP:
				smod += " | PULLUP";
				break;
			default:
				FatalError("bad pull setting on signal %s\n", it.first.c_str());
			}
			
			//Slew rate
			switch(it.second.m_drive)
			{
			case NocgenBSPPin::DRIVE_DEFAULT:
				break;
			case NocgenBSPPin::DRIVE_FAST:
				smod = " | SLEW = \"FAST\"";
				break;
			default:
				FatalError("bad slew setting on signal %s\n", it.first.c_str());
			}

			//Termination
			switch(it.second.m_term)
			{
			case NocgenBSPPin::TERM_NONE:
				//nothing
				break;
			case NocgenBSPPin::TERM_APPROX_25:
				smod += " | OUT_TERM = UNTUNED_25";
				break;
			case NocgenBSPPin::TERM_APPROX_50:
				smod += " | OUT_TERM = UNTUNED_50";
				break;
			case NocgenBSPPin::TERM_APPROX_75:
				smod += " | OUT_TERM = UNTUNED_75";
				break;
			default:
				FatalError("bad ter,omatopm setting on signal %s\n", it.first.c_str());
			}
			
			//I/O standard
			if(it.second.m_iostandard == "null")
				snprintf(tmp, sizeof(tmp), "NET \"%s\" LOC=%s;\n", it.first.c_str(), it.second.m_loc.c_str());
			else
			{
				snprintf(tmp, sizeof(tmp), "NET \"%s\" LOC=%s | IOSTANDARD = %s %s;\n",
					it.first.c_str(), it.second.m_loc.c_str(), it.second.m_iostandard.c_str(), smod.c_str());
			}
			node_pins += tmp;
		}
		for(auto it : m_clocknets)
		{
			//Add a timing name for each clock
			const char* name = it.first.c_str();
			snprintf(tmp, sizeof(tmp), "\nNET \"%s\" TNM_NET = \"%s\";\n", name, name);
			input_clocks += tmp;
			
			//skip derived clocks
			if(it.second.m_refclk != "")
				continue;

			string tname = str_replace("/", "_", it.first);
			snprintf(tmp, sizeof(tmp), "TIMESPEC TS_%s = PERIOD \"%s\" %.3f MHz HIGH %.0f%% INPUT_JITTER %dps;\n",
				tname.c_str(), name, it.second.m_freq / 1000000.0f, it.second.m_duty * 100, it.second.m_jitter);
			input_clocks += tmp;
		}
		
		//Add constraints for internal clock nets
		for(auto node : m_nodes)
		{
			string name = node->m_name;
			for(auto it : node->m_internalclocks)
			{
				snprintf(tmp, sizeof(tmp), "%s/%s", name.c_str(), it.first.c_str());
				string tname = str_replace("/", "_", tmp);
				string fqn = tmp;
				
				snprintf(tmp, sizeof(tmp), "\nNET \"%s\" TNM_NET = \"%s\";\n", fqn.c_str(), fqn.c_str());
				input_clocks += tmp;
				
				snprintf(tmp, sizeof(tmp), "TIMESPEC TS_%s = PERIOD \"%s\" %.3f MHz HIGH %.0f%%;\n",
				tname.c_str(), fqn.c_str(), it.second.m_freq / 1000000.0f, it.second.m_duty * 100);
				input_clocks += tmp;
			}
		}
		
		//Add cross-clock constraints
		for(auto x : m_crossclocks)
		{
			if(!IsClockNet(x.first))
				FatalError("invalid clock net %s for cross-clock path\n", x.first.c_str());
			if(!IsClockNet(x.second))
				FatalError("invalid clock net %s for cross-clock path\n", x.second.c_str());
			
			//Compute the max synchronizer delay allowed across the path
			//Set this to the min of the two clocks
			float clka_period = 1000000000.0f / m_clocknets[x.first].m_freq;
			float clkb_period = 1000000000.0f / m_clocknets[x.second].m_freq;
			float delay = min(clka_period, clkb_period);
			
			//Generate non-hierarchal names
			string clkas = str_replace("/", "_", x.first);
			string clkbs = str_replace("/", "_", x.second);
			
			//Generate the constraints
			string cross = template_get_contents("crossclock.ucf");
			snprintf(tmp, sizeof(tmp), "%.2f", delay);
			cross = str_replace("[[delay]]", tmp, cross);
			cross = str_replace("[[clka]]", x.first.c_str(), cross);
			cross = str_replace("[[clkb]]", x.second.c_str(), cross);
			cross = str_replace("[[clkas]]", clkas.c_str(), cross);
			cross = str_replace("[[clkbs]]", clkbs.c_str(), cross);
			
			crossclocks += cross;
		}
		
		//Add timing-ignore constraints
		for(auto x : m_tigs)
		{
			snprintf(tmp, sizeof(tmp), "NET \"%s\" TIG;\n", x.c_str());
			tigs += tmp;
		}
		
		//Map of pblock locations to pblock names
		map<string, string> pblocklocs;
		int pblocknum = 0;
		
		//Add floorplan constraints for each node
		for(auto x : m_nodes)
		{
			if(x->m_floorplans.size() != 0)
			{
				//Generate the list of locations
				string loclist;
				for(auto line : x->m_floorplans)
					loclist += line + "\n";
					
				//Check if we already have a pblock created for that location.
				//If not, create it
				string pblock_name = pblocklocs[loclist];
				if(pblock_name == "")
				{
					//Name the block
					snprintf(tmp, sizeof(tmp), "pblock_%d", pblocknum);
					pblocknum ++;
					pblock_name = tmp;
					pblocklocs[loclist] = pblock_name;
					
					//Create the constraints
					for(auto line : x->m_floorplans)
					{
						snprintf(tmp, sizeof(tmp), "AREA_GROUP \"%s\" RANGE=%s;\n", pblock_name.c_str(), line.c_str());
						floorplans += tmp;
					}
				}
				
				//Assign the location tp the pblock
				snprintf(tmp, sizeof(tmp), "INST \"%s\" AREA_GROUP = \"%s\";\n",
					x->m_name.c_str(), pblock_name.c_str());
				floorplans += tmp;
			}
		}
		
		//Add late floorplan constraints
		map<string, bool> pblocks_used;
		for(auto x : m_latefloorplans)
		{
			string node = x.first;
			auto planlist = x.second;
			
			//Generate the list of locations
			string loclist;
			for(auto line : planlist)
				loclist += line + "\n";
				
			//Check if we already have a pblock created for that location.
			//If not, create it
			string pblock_name = pblocklocs[loclist];
			if(pblock_name == "")
			{
				//Name the block
				snprintf(tmp, sizeof(tmp), "pblock_%d", pblocknum);
				pblocknum ++;
				pblock_name = tmp;
				pblocklocs[loclist] = pblock_name;
				
				//Create the constraints
				for(auto line : planlist)
				{
					snprintf(tmp, sizeof(tmp), "AREA_GROUP \"%s\" RANGE=%s;\n", pblock_name.c_str(), line.c_str());
					floorplans += tmp;
				}
			}
			
			//Assign the location tp the pblock
			snprintf(tmp, sizeof(tmp), "INST \"%s\" AREA_GROUP = \"%s\";\n",
				node.c_str(), pblock_name.c_str());
			floorplans += tmp;
		}	
	}
	
	//TODO: Support SDC constraints
	else
	{
		FatalError("SDC constraints not implemented yet\n");
	}
	m_constraints = str_replace("[[floorplans]]", floorplans, m_constraints);
	m_constraints = str_replace("[[tigs]]", tigs, m_constraints);
	m_constraints = str_replace("[[crossclocks]]", crossclocks, m_constraints);
	m_constraints = str_replace("[[node_pins]]", node_pins, m_constraints);
	m_constraints = str_replace("[[input_clocks]]", input_clocks, m_constraints);
}

void Nocgenv2GeneratedSourceNode::GenerateRouters()
{
	//Loop down the tree structure
	vector<NocgenQuadtreeNode*> nodes;
	nodes.push_back(m_rootRouter);
	
	string nocstuff;
	
	//Open graphviz markup
	m_dotcode = string("digraph ") + m_toplevel + " {\n";
	
	//Process each node
	char tmp[128];
	char tmp2[128];
	for(unsigned int i=0; i<nodes.size(); i++)
	{
		NocgenQuadtreeNode* node = nodes[i];
		
		NocgenRouter* router = dynamic_cast<NocgenRouter*>(node);
		if(router != NULL)
		{			
			//Read the template
			string routerpair;
			if(router->m_rpc)
				routerpair += template_get_contents("rpcrouter.v");
			if(router->m_dma)
				routerpair += template_get_contents("dmarouter.v");
			
			//Compute subnet mask
			unsigned int prefix_len = 16;
			unsigned int ntmp = router->m_subnetsize;
			while(ntmp > 1)
			{
				prefix_len --;
				ntmp /= 2;
			}
			unsigned int subnet_mask = 0xffff & ( ~(0xffff >> prefix_len) );
			
			//Top level config settings
			routerpair = str_replace("[[router_name]]", router->m_name.c_str(), routerpair);
			snprintf(tmp, sizeof(tmp), "%04x", router->m_baseaddr);
			routerpair = str_replace("[[base_addr]]", tmp, routerpair);
			snprintf(tmp, sizeof(tmp), "%04x", subnet_mask);
			routerpair = str_replace("[[subnet_mask]]", tmp, routerpair);
			snprintf(tmp, sizeof(tmp), "%d", 15 - prefix_len);
			routerpair = str_replace("[[host_bit_high]]", tmp, routerpair);
			
			//Generate uplink signals
			//Always present regardless of the port level etc
			string uplink_port_name = "root";
			if(router != m_rootRouter)
			{
				snprintf(tmp, sizeof(tmp), "router_%04x_%d", router->m_baseaddr, prefix_len);
				uplink_port_name = tmp;
			}
			snprintf(tmp, sizeof(tmp), "%s_rpc_rx_en", uplink_port_name.c_str());
				routerpair = str_replace("[[p4_rx_en]]", tmp, routerpair);
			snprintf(tmp, sizeof(tmp), "%s_rpc_rx_data", uplink_port_name.c_str());
				routerpair = str_replace("[[p4_rx_data]]", tmp, routerpair);
			snprintf(tmp, sizeof(tmp), "%s_rpc_rx_ack", uplink_port_name.c_str());
				routerpair = str_replace("[[p4_rx_ack]]", tmp, routerpair);
			snprintf(tmp, sizeof(tmp), "%s_rpc_tx_en", uplink_port_name.c_str());
				routerpair = str_replace("[[p4_tx_en]]", tmp, routerpair);
			snprintf(tmp, sizeof(tmp), "%s_rpc_tx_data", uplink_port_name.c_str());
				routerpair = str_replace("[[p4_tx_data]]", tmp, routerpair);
			snprintf(tmp, sizeof(tmp), "%s_rpc_tx_ack", uplink_port_name.c_str());
				routerpair = str_replace("[[p4_tx_ack]]", tmp, routerpair);
			snprintf(tmp, sizeof(tmp), "%s_dma_rx_en", uplink_port_name.c_str());
				routerpair = str_replace("[[dma_p4_rx_en]]", tmp, routerpair);
			snprintf(tmp, sizeof(tmp), "%s_dma_rx_data", uplink_port_name.c_str());
				routerpair = str_replace("[[dma_p4_rx_data]]", tmp, routerpair);
			snprintf(tmp, sizeof(tmp), "%s_dma_rx_ack", uplink_port_name.c_str());
				routerpair = str_replace("[[dma_p4_rx_ack]]", tmp, routerpair);
			snprintf(tmp, sizeof(tmp), "%s_dma_tx_en", uplink_port_name.c_str());
				routerpair = str_replace("[[dma_p4_tx_en]]", tmp, routerpair);
			snprintf(tmp, sizeof(tmp), "%s_dma_tx_data", uplink_port_name.c_str());
				routerpair = str_replace("[[dma_p4_tx_data]]", tmp, routerpair);
			snprintf(tmp, sizeof(tmp), "%s_dma_tx_ack", uplink_port_name.c_str());
				routerpair = str_replace("[[dma_p4_tx_ack]]", tmp, routerpair);	
			
			//Generate stuff for child ports
			string childports;
			for(int j=0; j<4; j++)
			{
				NocgenQuadtreeNode* child = node->m_children[j];
				
				bool rpc = false;
				bool dma = false;
				
				string child_name = "";
				
				if(child != NULL)
				{
					rpc = child->m_rpc;
					dma = child->m_dma;
					child_name = child->m_name;
					
					m_dotcode += router->m_name + " -> " + child_name + ";\n";
				}
				
				//Generate RPC port signals
				if(rpc)
				{
					snprintf(tmp, sizeof(tmp), "[[rpc_p%d_disable]]", j);
					routerpair = str_replace(tmp, "0", routerpair);
					
					//Generate the upstream signal declarations
					childports += str_replace("[[node_name]]", child_name, template_get_contents("rpc_wires.v"));
					
					snprintf(tmp2, sizeof(tmp2), "[[p%d_rx_en]]", j);
					snprintf(tmp, sizeof(tmp), "%s_rpc_tx_en", child_name.c_str());
					routerpair = str_replace(tmp2, tmp, routerpair);
					
					snprintf(tmp2, sizeof(tmp2), "[[p%d_rx_data]]", j);
					snprintf(tmp, sizeof(tmp), "%s_rpc_tx_data", child_name.c_str());
					routerpair = str_replace(tmp2, tmp, routerpair);
					
					snprintf(tmp2, sizeof(tmp2), "[[p%d_rx_ack]]", j);
					snprintf(tmp, sizeof(tmp), "%s_rpc_tx_ack", child_name.c_str());
					routerpair = str_replace(tmp2, tmp, routerpair);
					
					snprintf(tmp2, sizeof(tmp2), "[[p%d_tx_en]]", j);
					snprintf(tmp, sizeof(tmp), "%s_rpc_rx_en", child_name.c_str());
					routerpair = str_replace(tmp2, tmp, routerpair);
					
					snprintf(tmp2, sizeof(tmp2), "[[p%d_tx_data]]", j);
					snprintf(tmp, sizeof(tmp), "%s_rpc_rx_data", child_name.c_str());
					routerpair = str_replace(tmp2, tmp, routerpair);
					
					snprintf(tmp2, sizeof(tmp2), "[[p%d_tx_ack]]", j);
					snprintf(tmp, sizeof(tmp), "%s_rpc_rx_ack", child_name.c_str());
					routerpair = str_replace(tmp2, tmp, routerpair);
				}
				else
				{
					snprintf(tmp, sizeof(tmp), "[[rpc_p%d_disable]]", j);
					routerpair = str_replace(tmp, "1", routerpair);
					
					snprintf(tmp2, sizeof(tmp2), "[[p%d_tx_en]]", j);
					snprintf(tmp, sizeof(tmp), "rpc_router_%04x_%d_unused_tx_en[%d]",
						router->m_baseaddr, prefix_len, j);
					routerpair = str_replace(tmp2, tmp, routerpair);
					
					snprintf(tmp2, sizeof(tmp2), "[[p%d_tx_data]]", j);
					snprintf(tmp, sizeof(tmp), "rpc_router_%04x_%d_unused_tx_data[%d]",
						router->m_baseaddr, prefix_len, j);
					routerpair = str_replace(tmp2, tmp, routerpair);
					
					snprintf(tmp2, sizeof(tmp2), "[[p%d_tx_ack]]", j);
					routerpair = str_replace(tmp2, "2'b0", routerpair);
					
					snprintf(tmp2, sizeof(tmp2), "[[p%d_rx_en]]", j);
					routerpair = str_replace(tmp2, "1'b0", routerpair);
					
					snprintf(tmp2, sizeof(tmp2), "[[p%d_rx_data]]", j);
					routerpair = str_replace(tmp2, "32'h0", routerpair);
					
					snprintf(tmp2, sizeof(tmp2), "[[p%d_rx_ack]]", j);
					snprintf(tmp, sizeof(tmp), "rpc_router_%04x_%d_unused_rx_ack[%d]",
						router->m_baseaddr, prefix_len, j);
					routerpair = str_replace(tmp2, tmp, routerpair);
				}
				
				//Generate DMA port signals
				if(dma)
				{					
					snprintf(tmp, sizeof(tmp), "[[dma_p%d_disable]]", j);
					routerpair = str_replace(tmp, "0", routerpair);
					
					//Generate the upstream signal declarations
					childports += str_replace("[[node_name]]", child_name, template_get_contents("dma_wires.v"));
					
					snprintf(tmp2, sizeof(tmp2), "[[dma_p%d_rx_en]]", j);
					snprintf(tmp, sizeof(tmp), "%s_dma_tx_en", child_name.c_str());
					routerpair = str_replace(tmp2, tmp, routerpair);
					
					snprintf(tmp2, sizeof(tmp2), "[[dma_p%d_rx_data]]", j);
					snprintf(tmp, sizeof(tmp), "%s_dma_tx_data", child_name.c_str());
					routerpair = str_replace(tmp2, tmp, routerpair);
					
					snprintf(tmp2, sizeof(tmp2), "[[dma_p%d_rx_ack]]", j);
					snprintf(tmp, sizeof(tmp), "%s_dma_tx_ack", child_name.c_str());
					routerpair = str_replace(tmp2, tmp, routerpair);
					
					snprintf(tmp2, sizeof(tmp2), "[[dma_p%d_tx_en]]", j);
					snprintf(tmp, sizeof(tmp), "%s_dma_rx_en", child_name.c_str());
					routerpair = str_replace(tmp2, tmp, routerpair);
					
					snprintf(tmp2, sizeof(tmp2), "[[dma_p%d_tx_data]]", j);
					snprintf(tmp, sizeof(tmp), "%s_dma_rx_data", child_name.c_str());
					routerpair = str_replace(tmp2, tmp, routerpair);
					
					snprintf(tmp2, sizeof(tmp2), "[[dma_p%d_tx_ack]]", j);
					snprintf(tmp, sizeof(tmp), "%s_dma_rx_ack", child_name.c_str());
					routerpair = str_replace(tmp2, tmp, routerpair);
				}
				else
				{
					snprintf(tmp, sizeof(tmp), "[[dma_p%d_disable]]", j);
					routerpair = str_replace(tmp, "1", routerpair);
					
					snprintf(tmp2, sizeof(tmp2), "[[dma_p%d_tx_en]]", j);
					snprintf(tmp, sizeof(tmp), "dma_router_%04x_%d_unused_tx_en[%d]",
						router->m_baseaddr, prefix_len, j);
					routerpair = str_replace(tmp2, tmp, routerpair);
					
					snprintf(tmp2, sizeof(tmp2), "[[dma_p%d_tx_data]]", j);
					snprintf(tmp, sizeof(tmp), "dma_router_%04x_%d_unused_tx_data[%d]",
						router->m_baseaddr, prefix_len, j);
					routerpair = str_replace(tmp2, tmp, routerpair);
					
					snprintf(tmp2, sizeof(tmp2), "[[dma_p%d_tx_ack]]", j);
					routerpair = str_replace(tmp2, "1'b0", routerpair);

					snprintf(tmp2, sizeof(tmp2), "[[dma_p%d_rx_en]]", j);
					routerpair = str_replace(tmp2, "1'b0", routerpair);
					
					snprintf(tmp2, sizeof(tmp2), "[[dma_p%d_rx_data]]", j);
					routerpair = str_replace(tmp2, "32'h0", routerpair);
					
					snprintf(tmp2, sizeof(tmp2), "[[dma_p%d_rx_ack]]", j);
					snprintf(tmp, sizeof(tmp), "dma_router_%04x_%d_unused_rx_ack[%d]",
						router->m_baseaddr, prefix_len, j);
					routerpair = str_replace(tmp2, tmp, routerpair);
				}
			}
			
			//Push our children onto the list of nodes
			for(int j=0; j<4; j++)
			{
				if(node->m_children[j] != NULL)
					nodes.push_back(node->m_children[j]);
			}
			
			//Done, append it
			nocstuff += childports + routerpair;
		}
		else
			nocstuff += GenerateNode(dynamic_cast<NocgenNode*>(node));
	}
	
	//Done
	m_code = str_replace("[[nocstuff]]", nocstuff, m_code);
	
	//Finally, go back and patch back-references between nodes
	for(auto node : m_nodes)
	{
		snprintf(tmp, sizeof(tmp), "[[node_%s_addr]]", node->m_name.c_str());
		snprintf(tmp2, sizeof(tmp2), "16'h%04x", node->m_baseaddr);
		m_code = str_replace(tmp, tmp2, m_code);
	}
	
	//Close graphviz markup
	m_dotcode += "}\n";
}

void Nocgenv2GeneratedSourceNode::GenerateNameserverTable()
{
	string host_mem;
	string addr_mem;
	
	//Sanity check
	if(m_nodes.size() > 256)
		FatalError("Too many nodes for name server (max supported in current rev is 256)\n");
	
	//Format the stuff
	for(unsigned int i=0; i<m_nodes.size(); i++)
	{
		unsigned char name[9] = {0};
		strncpy((char*)name, m_nodes[i]->m_name.c_str(), 8);
		char hname[18] = {0};
		snprintf(hname, sizeof(hname), "%02x%02x%02x%02x%02x%02x%02x%02x\n",
			name[0], name[1], name[2], name[3], name[4], name[5], name[6], name[7]);
		host_mem += hname;
		
		char haddr[6];
		snprintf(haddr, sizeof(haddr), "%04x\n", m_nodes[i]->m_baseaddr);
		addr_mem += haddr;
		
		//Sanity check
		if(m_nodes[i]->m_name.length() > 8)
			FatalError("Name \"%s\" is too long (max is 8 chars)\n", m_nodes[i]->m_name.c_str());
	}
	
	//Pad out to 256 entries
	for(unsigned int i=m_nodes.size(); i<256; i++)
	{
		addr_mem += "0000\n";
		host_mem += "0000000000000000\n";
	}
	
	//Save addresses
	string ofname = GetNameserverAddrPath();
	MakeDirectoryRecursive(GetDirOfFile(ofname), 755);
	FILE* fp = fopen(ofname.c_str(), "w");
	if(!fp)
		FatalError("Failed to create nameserver addr file %s\n", ofname.c_str());
	fputs(addr_mem.c_str(), fp);
	fclose(fp);
	
	//Save host info
	ofname = GetNameserverHostPath();
	 fp = fopen(ofname.c_str(), "w");
	if(!fp)
		FatalError("Failed to create nameserver host file %s\n", ofname.c_str());
	fputs(host_mem.c_str(), fp);
	fclose(fp);
}

string Nocgenv2GeneratedSourceNode::GenerateNode(NocgenNode* node)
{
	if(node == NULL)
		FatalError("invalid nocgen node");
	
	string code = template_get_contents("node.v");
	
	//Replace the easy stuff
	code = str_replace("[[node_name]]", node->m_name, code);
	code = str_replace("[[node_type]]", node->m_module, code);
	
	//Address
	char tmp[4096];
	snprintf(tmp, sizeof(tmp), "16'h%04x", node->m_baseaddr);
	code = str_replace("[[node_addr]]", tmp, code);
	
	//Ports
	string node_ports;
	for(auto it : node->m_ports)
	{
		snprintf(tmp, sizeof(tmp), "\t\t.%s(%s),\n", it.first.c_str(), it.second.c_str());
		node_ports += tmp;
	}
	if(node->m_rpc)
		node_ports += template_get_contents("rpc_ports.v");
	if(node->m_dma)
		node_ports += template_get_contents("dma_ports.v");
	node_ports = str_replace("[[node_name]]", node->m_name, node_ports);
	
	//Parameters
	string node_params;
	for(auto it : node->m_params)
	{
		snprintf(tmp, sizeof(tmp), "\t\t.%s(%s),\n", it.first.c_str(), it.second.c_str());
		node_params += tmp;
	}
	
	//Patch them in
	code = str_replace("[[node_params]]", node_params, code);
	code = str_replace("[[node_ports]]", node_ports, code);
	return code;
}

string Nocgenv2GeneratedSourceNode::template_get_contents(string fname)
{
	string nocgen_root = m_graph->GetSourceDir() + "/rtl/nocgen/";
	fname = nocgen_root + fname;
	FILE* fp = fopen(fname.c_str(), "rb");
	if(!fp)
		FatalError("file_get_contents: Could not open file \"%s\"\n", fname.c_str());
	fseek(fp, 0, SEEK_END);
	long fsize = ftell(fp);
	fseek(fp, 0, SEEK_SET);
	char* buf = new char[fsize + 1];
	buf[fsize] = 0;
	fread(buf, 1, fsize, fp);
	fclose(fp);
	string tmp(buf);
	delete[] buf;
	return tmp;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Accessors

string Nocgenv2GeneratedSourceNode::GetOutputPath()
{
	return m_graph->GetOutDir() + "/generic/nocgen/" + m_fname;
}

string Nocgenv2GeneratedSourceNode::GetConstraintsPath()
{
	return m_graph->GetOutDir() + "/generic/nocgen/" + GetBasenameOfFileWithoutExt(m_fname) + ".ucf";
}

string Nocgenv2GeneratedSourceNode::GetDotPath()
{
	return m_graph->GetOutDir() + "/generic/nocgen/graphviz/" + GetBasenameOfFileWithoutExt(m_fname) + ".dot";
}

string Nocgenv2GeneratedSourceNode::GetNameserverHostPath()
{
	return m_graph->GetOutDir() + "/generic/nocgen/" + GetBasenameOfFileWithoutExt(m_fname) + "_host_mem.txt";
}

string Nocgenv2GeneratedSourceNode::GetNameserverAddrPath()
{
	return m_graph->GetOutDir() + "/generic/nocgen/" + GetBasenameOfFileWithoutExt(m_fname) + "_addr_mem.txt";
}

int Nocgenv2GeneratedSourceNode::GetSignalWidth(string name)
{
	if(m_buswidths.find(name) != m_buswidths.end())
		return m_buswidths[name];
	
	//TODO fix this - KLUDGY!!!
	else if(name.find("_rpc_tx_en") != string::npos)
		return 1;
	else if(name.find("_rpc_tx_ack") != string::npos)
		return 2;
	else if(name.find("_rpc_tx_data") != string::npos)
		return 32;
	else if(name.find("_rpc_rx_en") != string::npos)
		return 1;
	else if(name.find("_rpc_rx_ack") != string::npos)
		return 2;
	else if(name.find("_rpc_rx_data") != string::npos)
		return 32;
		
	else if(name.find("_dma_tx_en") != string::npos)
		return 1;
	else if(name.find("_dma_tx_ack") != string::npos)
		return 1;
	else if(name.find("_dma_tx_data") != string::npos)
		return 32;
	else if(name.find("_dma_rx_en") != string::npos)
		return 1;
	else if(name.find("_dma_rx_ack") != string::npos)
		return 1;
	else if(name.find("_dma_rx_data") != string::npos)
		return 32;
		
	else
		FatalError("signal %s doesn't have a known width\n", name.c_str());
}

string Nocgenv2GeneratedSourceNode::GetDescription()
{
	char desc[1024];
	snprintf(desc, sizeof(desc), "NocGenV2 top-level module %s", m_fname.c_str());
	return string(desc);
}

string Nocgenv2GeneratedSourceNode::GetBinaryPath()
{
	return GetOutputPath();
}

string Nocgenv2GeneratedSourceNode::GetHash()
{
	//Make sure we have valid dependencies
	UpdateDependencies();
	
	//Get hash of our dependencides
	string hash = m_srcfile->GetHashCached();
	
	//Hash our generated code
	hash += sha256(m_code);
	hash += sha256(m_constraints);
		
	//Add data for our dependencies
	for(size_t i=0; i<m_dependencies.size(); i++)
		hash += m_dependencies[i]->GetHashCached(); 
			
	//Done
	return sha256(hash);
}

void Nocgenv2GeneratedSourceNode::Update(Cluster* cluster)
{
	//Rebuild all of our dependencies if necessary
	vector<ClusterJob*> deps;
	for(size_t i=0; i<m_dependencies.size(); i++)
	{		
		BuildGraphNode* node = m_dependencies[i];
		node->UpdateCached(cluster);
		if(node->GetBuildJob() != NULL)
			deps.push_back(node->GetBuildJob());
	}
}

void Nocgenv2GeneratedSourceNode::UpdateDependencies()
{
	VerilogGeneratedSourceNode::UpdateDependencies();
	
	//Add extra target dependencies
	for(auto x : m_dtargets)
		m_dependencies.push_back(m_graph->GetTarget(x));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Factory method

Nocgenv2GeneratedSourceNode* Nocgenv2GeneratedSourceNode::CreateNocgenNode(BuildGraph* graph, string script, string bsp)
{
	//See if the source file has been used. If not, create a node for it.
	script = CanonicalizePath(script);
	CodeGenScriptNode* srcnode = dynamic_cast<CodeGenScriptNode*>(graph->GetNodeForPath(script, "generic"));
	if(srcnode == NULL)
	{
		srcnode = new CodeGenScriptNode(graph, script);
		graph->AddNodeWithPath(srcnode, script, "generic");
	}
	
	//See if the source file has been used. If not, create a node for it.
	bsp = CanonicalizePath(bsp);
	CodeGenScriptNode* bspnode = dynamic_cast<CodeGenScriptNode*>(graph->GetNodeForPath(bsp, "generic"));
	if(bspnode == NULL)
	{
		bspnode = new CodeGenScriptNode(graph, bsp);
		graph->AddNodeWithPath(bspnode, bsp, "generic");
	}
	
	//Generate output file name
	string outfname = GetBasenameOfFileWithoutExt(script) + "_" + GetBasenameOfFileWithoutExt(bsp) + ".v";
		
	//Create the nocgen node itself
	Nocgenv2GeneratedSourceNode* node = new Nocgenv2GeneratedSourceNode(graph, srcnode, bspnode, outfname);
	graph->AddNodeWithPath(node, node->GetOutputPath(), "generic");

	//Done
	return node;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Netlist accessors

bool Nocgenv2GeneratedSourceNode::IsBSPClockNet(string str)
{
	return (m_bspclocks.find(str) != m_bspclocks.end());
}

bool Nocgenv2GeneratedSourceNode::IsClockNet(string str)
{
	return (m_clocknets.find(str) != m_clocknets.end());
}

void Nocgenv2GeneratedSourceNode::AddClockNet(string name, NocgenClockNet clk)
{
	if(m_clocknets.find(name) != m_clocknets.end())
		FatalError("Attempted redeclaration of clock net \"%s\"", name.c_str());
		
	m_clocknets[name] = clk;
}

void Nocgenv2GeneratedSourceNode::AddClockNetFromBSP(string name)
{
	AddClockNet(name, m_bspclocks[name]);
	
	AddPinFromBSP(name, NocgenBSPPin::DIR_IN);
}

void Nocgenv2GeneratedSourceNode::AddPinFromBSP(string name, unsigned int dir)
{
	//If it's a debug pin, do special stuff
	if(dir == NocgenBSPPin::DIR_NC)
	{
		m_toppins[name] = NocgenBSPPin(name, "", "", dir);
		if(name.find("[") == string::npos)
			m_buswidths[name] = 1;
		return;
	}
	
	//Verify the signal is in the bsp unless we're adding a debug pin
	if(m_bsppins.find(name) == m_bsppins.end())
		FatalError("Pin \"%s\" is not in the BSP\n", name.c_str());
		
	//Look up the BSP pin and make sure it has a compatible direction
	NocgenBSPPin& p = m_bsppins[name];
	if( (dir & p.m_dir) != dir)
		FatalError("Pin \"%s\" has an incompatible direction\n", name.c_str());
		
	//If the pin has location "CCLK" then we need to do special stuff
	if(p.m_loc == "CCLK")
		m_cclknet = name;
		
	//Otherwise add a top-level port for it
	else
	{
		m_toppins[name] = p;
		m_toppins[name].m_dir = dir;
	}
	
	//Record width
	if(name.find("[") == string::npos)
		m_buswidths[name] = 1;
}

void Nocgenv2GeneratedSourceNode::AddBusFromBSP(string name, unsigned int width, unsigned int dir)
{
	char tmp[128];
	for(unsigned int i=0; i<width; i++)
	{
		snprintf(tmp, sizeof(tmp), "%s[%d]", name.c_str(), i);
		AddPinFromBSP(tmp, dir);
	}
	
	m_buswidths[name] = width;
}

NocgenDeviceFamily Nocgenv2GeneratedSourceNode::GetDeviceFamily()
{
	char devname[128];
	int speed;
	char pkgstuff[128];
	if(3 != sscanf(m_device.c_str(), "%127[^-]-%d%127s", devname, &speed, pkgstuff))
		FatalError("Invalid nocgen device name \"%s\"\n", devname);
	
	if(strstr(devname, "xc6s"))
		return FAMILY_SPARTAN6;
	else if(strstr(devname, "xc7a"))
		return FAMILY_ARTIX7;
	else if(strstr(devname, "xc7k"))
		return FAMILY_KINTEX7;
	else
		FatalError("Invalid nocgen device name \"%s\"\n", devname);
}

int Nocgenv2GeneratedSourceNode::GetSpeedGrade()
{
	char devname[128];
	int speed;
	char pkgstuff[128];
	if(3 != sscanf(m_device.c_str(), "%127[^-]-%d%127s", devname, &speed, pkgstuff))
		FatalError("Invalid nocgen device name \"%s\"\n", devname);	
	return speed;
}
